﻿using System;
using System.Collections;
using System.Text;
using System.Xml;
using System.Globalization;

namespace Portal.Api.FeedProvider
{
  /// <summary>
  /// Wrapper for an RSS Feed.
  /// </summary>
  public class RssFeed : XmlTextWriter
  {
    /// <summary>
    /// Is the Guid Attribute a permalink?
    /// </summary>
    public bool GuidIsPermaLink { get; set; }

    /// <summary>
    /// A Delegate to extend the data of an item.
    /// </summary>
    /// <param name="item">The item that is added to the feed.</param>
    /// <param name="rssFeed">The resulting feed.</param>
    public delegate void ExtensionDelegate(object item, RssFeed rssFeed);


    public RssFeed(System.IO.Stream outputStream)
      : base(outputStream, Encoding.UTF8)
    {
      GuidIsPermaLink = true;

      WriteStartDocument();
      WriteWhitespace(Environment.NewLine);
      WriteStartElement("rss");
      WriteAttributeString("version", "2.0");
      WriteAttributeString("xmlns", "atom", null, "http://www.w3.org/2005/Atom");

      WriteWhitespace(Environment.NewLine);
      WriteStartElement("channel");
      WriteWhitespace(Environment.NewLine);
    }

    public RssFeed(System.IO.Stream outputStream, string feedAddress)
      : this(outputStream)
    {
      WriteStartElement("atom", "link", null);
      WriteAttributeString("href", feedAddress);
      WriteAttributeString("rel", "self");
      WriteAttributeString("type", "application/rss+xml");
      WriteEndElement();
      WriteWhitespace(Environment.NewLine);
    }

    public override void Close()
    {
      WriteEndElement();
      WriteWhitespace(Environment.NewLine);
      WriteEndDocument();
      
      base.Close();
    }

    /// <summary>
    /// Add a title in the header part of the rss feed.
    /// </summary>
    /// <param name="title">The title</param>
    public void AddTitle(string title)
    {
      WriteElementString("title", title);
      WriteWhitespace(Environment.NewLine);
    }


    /// <summary>
    /// Add an url in the header part of the rss feed.
    /// </summary>
    /// <param name="url">The url</param>
    public void AddUrl(string url)
    {
      WriteElementString("link", url);
      WriteWhitespace(Environment.NewLine);
    }


    /// <summary>
    /// Add a description in the header part of the rss feed.
    /// </summary>
    /// <param name="description">The description.</param>
    public void AddDescription(string description)
    {
      WriteElementString("description", description);
      WriteWhitespace(Environment.NewLine);
    }

    /// <summary>
    /// Add a language in the header part of the rss feed.
    /// </summary>
    /// <param name="language"></param>
    public void AddLanguage(string language)
    {
      WriteElementString("language", language);
      WriteWhitespace(Environment.NewLine);
    }


    /// <summary>
    /// Add a copyright in the header part of the rss feed.
    /// </summary>
    /// <param name="language"></param>
    public void AddCopyright(string copyright)
    {
      WriteElementString("copyright", copyright);
      WriteWhitespace(Environment.NewLine);
    }

    /// <summary>
    /// Add a generator information in the header part of the rss feed.
    /// </summary>
    /// <param name="p"></param>
    public void AddGenerator(string generator)
    {
      WriteElementString("generator", generator);
      WriteWhitespace(Environment.NewLine);
    }

    /// <summary>
    /// Add a Time to live information in the header part of the rss feed.
    /// </summary>
    /// <param name="time">Time in minutes</param>
    public void AddTimeToLive(int time)
    {
      WriteElementString("ttl", time.ToString(CultureInfo.InvariantCulture));
      WriteWhitespace(Environment.NewLine);
    }

    /// <summary>
    /// Add the specific items to the rss feed.
    /// </summary>
    /// <param name="items">The items to add</param>
    public void AddItems(IEnumerable items)
    {
      AddItems(items, null);
    }

    /// <summary>
    /// Add the specific items to the rss feed.
    /// </summary>
    /// <param name="items">The items to add</param>
    /// <param name="extensionFunc">A delegate that is called for each item to extend the rss feed.</param>
    public void AddItems(IEnumerable items, ExtensionDelegate extensionFunc)
    {
      foreach (var item in items)
      {
        WriteStartElement("item");
        foreach (var prop in item.GetType().GetProperties())
        {
          object[] attrs = prop.GetCustomAttributes(typeof(RssElementAttribute), true);
          if (attrs != null)
          {
            foreach (RssElementAttribute attribute in attrs)
            {
              WriteItemElement(attribute.ItemElement, prop.GetValue(item, null).ToString());
            }
          }
        }
        if (extensionFunc != null)
          extensionFunc(item, this);
        WriteEndElement();
        WriteWhitespace(Environment.NewLine);
      }
    }

    /// <summary>
    /// Writes an Element with a value for a rssItem.
    /// </summary>
    /// <param name="itemElement"></param>
    /// <param name="value"></param>
    public void WriteItemElement(ItemElementType itemElement, string value)
    {
      // The Guid is a special case with the additional attribute.
      if(itemElement == ItemElementType.guid)
      {
        WriteStartElement(itemElement.ToString());
        WriteAttributeString("isPermaLink", XmlConvert.ToString(GuidIsPermaLink));
        WriteString(value);
        WriteEndElement();
      }
      else
        WriteElementString(itemElement.ToString(), value.ToString());
    }

    /// <summary>
    /// Writes an Startelement with a value for a rssItem.
    /// </summary>
    /// <param name="itemElement"></param>
    public void WriteItemEnclosure(string url, long length, string mimeType)
    {
      WriteStartElement(ItemElementType.enclosure.ToString());
      WriteAttributeString("url", url);
      WriteAttributeString("length", length.ToString(CultureInfo.InvariantCulture));
      WriteAttributeString("type", mimeType);
      WriteEndElement();
    }
  }
}
